#include "Geogame.h"

Geogame::Geogame()
{
    screenName = "Geogame";

    std::cout << "Starting Geogame editor." << std::endl;

    // Initial variables
    mode = 0;
    mouseX = mouseY = 0;
    hitboxRange = 25;
    selectedPoint = 0;
    selectedPointIndex = 0;
    isSelected = false;

    sceneMoveDist = 50.0f;
}

void Geogame::draw()
{
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

    // For debugging: Adding a background to show the layering of the different scenes
    //DrawHelper::Instance().drawRect2D(0, 0, 300, 300, new Color(1,1,1,1), true);

	glBindTexture(GL_TEXTURE_2D, 0);
	glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
	if (mode == 2)
	{
	    // Draw collision line strip in semi-transparent red
        glColor4f(1.0f, 0.0f, 0.0f, 0.25f);
        glBegin(GL_LINE_STRIP);
            for (unsigned int i = 0; i < points.size(); i++)
            {
                glVertex2i(points[i]->first, points[i]->second);
            }
        glEnd();
        // Draw smoothed line
        glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
        glBegin(GL_LINE_STRIP);
            for (unsigned int i = 0; i < smoothPoints.size(); i++)
            {
                glVertex2i(smoothPoints[i]->first, smoothPoints[i]->second);
            }
        glEnd();
	}
	else
	{
        glBegin(GL_LINE_STRIP);
            for (unsigned int i = 0; i < points.size(); i++)
            {
                glVertex2i(points[i]->first, points[i]->second);
            }
        glEnd();
	}
	if (mode == 1)
	{
        // A second pass must be done for now, oh well.
        for (unsigned int i = 0; i < points.size(); i++)
        {
            if (points[i] == selectedPoint)
            {
                // Change color of point if it has been selected
                if (isSelected)
                    glColor4f(1.0f, 0.0f, 0.0f, 1.0f);

                glRecti(points[i]->first-3, points[i]->second-3, points[i]->first+3, points[i]->second+3);
                glColor4f(0.0f, 0.0f, 0.0f, 1.0f); // Revert back to old color
            }
            else
            {
                glRecti(points[i]->first-2, points[i]->second-2, points[i]->first+2, points[i]->second+2);
            }
        }
	}
}

void Geogame::update(Uint32* deltaTime)
{
    // Only bother if mouse is down
    if (mode == 1 && !isSelected)
    {
        // Find a point to select if not selected
        for (unsigned int i = 0; i < points.size(); i++)
        {
            // If multiple points are found in the hitbox range, the last point ends up as the selected point
            if ((mouseX-hitboxRange < points[i]->first) && (mouseX+hitboxRange > points[i]->first) &&
                    (mouseY-hitboxRange < points[i]->second) && (mouseY+hitboxRange > points[i]->second))
            {
                // Use this vertex as our selected point.
                selectedPoint = points[i];
                selectedPointIndex = i;
            }
        }
    }
}

void Geogame::handleEvents(SDL_Event* event)
{
    mouseX = event->motion.x-x;
    mouseY = event->motion.y-y;

    if (event->type == SDL_MOUSEBUTTONDOWN)
    {
        // Create new point
        if (mode == 0)
        {
            createPoint();
            std::cout << "Clicked at " << mouseX << ", " << mouseY << std::endl;
        }
        else if (mode == 1)
        {
            // Select and edit a point
            if (isSelected)
            {
                // Update selected position with new coordinates designated by the "unclick"
                std::cout << "Moved vertex from (" << points[selectedPointIndex]->first << ","
                        << points[selectedPointIndex]->second << ") ";
                movePoint();
                std::cout << "to (" << points[selectedPointIndex]->first << ","
                        << points[selectedPointIndex]->second << ") " << std::endl;
                isSelected = false;
            }
            else
            {
                // Indicate that the user has selected a point
                isSelected = true;
                std::cout << "Selected vertex (" << selectedPoint->first << ","
                        << selectedPoint->second << ")" << std::endl;
            }
        }
    }
    else if (event->type == SDL_KEYDOWN)
    {
        // Switch modes
        if (event->key.keysym.sym == SDLK_m)
        {
            mode++;
            if (mode == 2)
                smoothLine(); // Smooth line again in case it changed
            if (mode > 2)
                mode = 0;
            std::cout << "Switched modes: " << mode << std::endl;
        }
        else if (event->key.keysym.sym == SDLK_s)
        {
            save("./test.map");
        }
        else if (event->key.keysym.sym == SDLK_l)
        {
            load("./test.map");
        }
        // Move screen
        else if (event->key.keysym.sym == SDLK_RIGHT)
        {
            x -= sceneMoveDist;
        }
        else if (event->key.keysym.sym == SDLK_LEFT)
        {
            x += sceneMoveDist;
        }
        else if (event->key.keysym.sym == SDLK_UP)
        {
            y += sceneMoveDist;
        }
        else if (event->key.keysym.sym == SDLK_DOWN)
        {
            y -= sceneMoveDist;
        }
        // Move back to main screen
        else if (event->key.keysym.sym == SDLK_ESCAPE)
        {
            // Create and add new screen
            MainMenu* menu = new MainMenu();
            Engine::Instance().addScreen2D(menu);
            // Turn this screen off
            visible = updatable = eventable = false;
        }
    }
}

void Geogame::createPoint()
{
    Geopoint* position = new Geopoint(mouseX, mouseY);
    // Determine line slope between previous and new point
    if (points.size() > 0)
    {
        // (Interesting that splitting this slope formula up pervents division by zero issues)
        float temp = (points[ (points.size()-1) ]->first - mouseX);
        position->slope = (points[ (points.size()-1) ]->second - mouseY) / temp;
    }
    points.push_back(position);
}

void Geogame::movePoint()
{
    // Update selected position with new coordinates
    points[selectedPointIndex]->first = mouseX;
    points[selectedPointIndex]->second = mouseY;
    // Adjust new line slope between previous and this point
    if (selectedPointIndex > 0)
    {
        // (Interesting that splitting this slope formula up pervents division by zero issues)
        float temp = (points[ (selectedPointIndex-1) ]->first - mouseX);
        points[selectedPointIndex]->slope = (points[ (selectedPointIndex-1) ]->second - mouseY) / temp;
    }
}

// Line smoothing using the McMaster Slide Averaging Algorithm
// Later we can do something more complex.
void Geogame::smoothLine()
{
    // Start fresh, delete all points stored in smoothPoints
    smoothPoints.clear();

    // Return early if there is nothing to do
    if (points.size() < 5)
    {
        std::cout << "Nothing to smooth." << std::endl;
        return ;
    }

    // Add first two points point
    Geopoint* position1 = new Geopoint(points[0]->first, points[0]->second); // No need to copy slope
    smoothPoints.push_back(position1);

    Geopoint* position2 = new Geopoint(points[1]->first, points[1]->second); // No need to copy slope
    smoothPoints.push_back(position2);

    float tempX, tempY;

    // Don't iterate through the last two, since we can't average them.
    for (unsigned int i = 2; i < (points.size()-2); i++)
    {
        // Average first five points
        tempX = (points[(i-2)]->first + points[(i-1)]->first + points[i]->first + points[(i+1)]->first + points[(i+2)]->first) / 5;
        tempY = (points[(i-2)]->second + points[(i-1)]->second + points[i]->second + points[(i+1)]->second + points[(i+2)]->second) / 5;

        // Find the midpoint between our average point and the middle point of our five points
        tempX = (points[i]->first + tempX) / 2;
        tempY = (points[i]->second + tempY) / 2;

        // Add our new point
        Geopoint* position = new Geopoint(tempX, tempY);
        smoothPoints.push_back(position);
    }

    // Add last two points point
    position1 = new Geopoint(points[(points.size()-2)]->first, points[(points.size()-2)]->second); // No need to copy slope
    smoothPoints.push_back(position1);

    position2 = new Geopoint(points[(points.size()-1)]->first, points[(points.size()-1)]->second); // No need to copy slope
    smoothPoints.push_back(position2);

    std::cout << "Draw line smoothed." << std::endl;
}

bool Geogame::save(std::string sfile)
{
    std::ifstream file(sfile.c_str());

    if (file != 0)
    {
        std::cout << "Save file already exists. Contents will be destroyed." << std::endl;
    }

    {
        std::ofstream file(sfile.c_str());
        file << points.size() << std::endl;
        for (unsigned int i = 0; i < points.size(); i++)
        {
            file << points[i]->first << " " << points[i]->second << " " << points[i]->slope << std::endl;
        }
    }

    file.close();

    std::cout << "Map saved." << std::endl;

    return true;
}

bool Geogame::load(std::string lfile)
{
    points.clear(); // Remove all current points
    std::ifstream file(lfile.c_str());

    if (file != 0)
    {
        int vSize;
        int pointX, pointY;
        float pointSlope;
        Geopoint* position;
        file >> vSize;
        for (int i = 0; i < vSize; i++)
        {
            file >> pointX;
            file >> pointY;
            file >> pointSlope;

            position = new Geopoint(pointX, pointY);
            position->slope = pointSlope;
            points.push_back(position);
        }

        smoothLine();

        std::cout << "Map loaded." << std::endl;
    }
    else
    {
        std::cout << "Error loading map from file." << std::endl;
    }

    file.close();

    return true;
}

